package oceanlife;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashMap;

import javax.swing.JLabel;
import javax.swing.JPanel;

import oceanlife.evostrat.Gene;
import oceanlife.log.FCLogger;
import oceanlife.pathfinding.Edge;
import oceanlife.pathfinding.Line2;
import oceanlife.pathfinding.Pillar;
import oceanlife.pathfinding.Pnt;
import oceanlife.pathfinding.PointGraph;
import oceanlife.pathfinding.SimpleGraphBuilder;
import oceanlife.pathfinding.SimpleObstacle;



@SuppressWarnings("serial")
public class SimpleSimPanel extends JPanel{
	public ArrayList<Pnt> getAgentScenarioGoals() {
		return agentScenarioGoals;
	}

	//scenario-density-time-ID
	private String ID;
	
	private ArrayList<Agent> agents = new ArrayList<Agent>();
	private ArrayList<Line2> graphLines = new ArrayList<Line2>();
	private ArrayList<SimpleObstacle> simpleObs = new ArrayList<SimpleObstacle>();
	
	//scenario/group (left/right side of screen)/individual
	private ArrayList<ArrayList<ArrayList<Agent>>> agentScenarios = new ArrayList<ArrayList<ArrayList<Agent>>>();
	private ArrayList<Pnt> agentScenarioGoals = new ArrayList<Pnt>();
	
	private SimpleGraphBuilder sGB;
	
	private int scenarios = 0;
	private int sides = 0;
	private int currentScenario = 0;
	private int totalCycles = 0;
	private int scenarioCycles = 0;
	private int totalAgentCycles = 0;
	private int agentCycles = 0;
	private boolean running = true;
	
	public HashMap<Integer,Integer> fitnesses = new HashMap<Integer,Integer>();
	
	public int children;
	private ArrayList<ArrayList<Boolean>> agentsSpawned = null;
	
	private Gene gene;
	private RandomGen rand;

	
	public SimpleSimPanel(int children){
		clear();
		this.children = children;
	}
	
	
	
	public synchronized void clear(){
		agents = new ArrayList<Agent>();
		graphLines = new ArrayList<Line2>();
		simpleObs = new ArrayList<SimpleObstacle>();
		totalCycles = 0;
		scenarioCycles = 0;
		totalAgentCycles = 0;
		agentCycles = 0;
		sGB = new SimpleGraphBuilder(simpleObs);
		gene = null;
		rand = null;
		ID = null;
		this.repaint();
	}
	
	public synchronized void paintComponent(Graphics g) {
		clear(g);
		Graphics2D g2d = (Graphics2D)g;
		g2d.draw(new Rectangle2D.Double(0,0,OLSet.getX_size()-1.0,OLSet.getY_size()-1.0));
		g2d.setColor(Color.gray);
		//for(int i = 0; i < obs.getPolygonCount(); i++){
		//	g2d.draw(obs.getPolygon(i));
		//	g2d.fill(obs.getPolygon(i));
		//}
		try{
			for(SimpleObstacle ob : simpleObs){
				g2d.setColor(Color.green);
				//g2d.draw(ob.pathToPolygon());
				g2d.setColor(Color.gray);
				g2d.draw(ob.obstacleToPolygon());
				g2d.fill(ob.obstacleToPolygon());
				/*for(SimpleObstacle ob2 : simpleObs){
					if(!ob.equals(ob2)){
						Line2 connection = ob.getShortestLineTo(ob2);
						if(connection != null){
							g2d.setColor(Color.pink);
							g2d.draw(connection);
						}
					}
				}*/
			}
			
			Font font = new Font("Dialog", Font.PLAIN, 10);
			g2d.setFont(font);
			/*for(Line2 line : graphLines){
				g2d.setColor(Color.blue);
				g2d.draw(line);
				g2d.setColor(Color.red);
				Edge edge = sGB.getGraph().getTheNeighbors().get(line.getStart()).get(line.getEnd());
				int x = (int)((edge.getFirstPoint().coord(0)+edge.getLastPoint().coord(0))/2.0);
				int y = (int)((edge.getFirstPoint().coord(1)+edge.getLastPoint().coord(1))/2.0);
				g2d.drawString(((int)edge.agentsOnEdge.size())+"",x,y);
			}*/
			for(int agNum = 0; agNum < agents.size(); agNum++){
				Agent ag = agents.get(agNum);
				g2d.setColor(ag.getColor());
				g2d.fill(ag.getPositionGraphic());
				g2d.setColor(Color.black);
				g2d.draw(ag.getPositionGraphic());
				g2d.draw(ag.getDirectionGraphic());
				if(ag.obsPoints != null){
					g2d.setColor(Color.yellow);
					//for(RadialFace rf : ag.obsPoints){
					//	g2d.draw(new Ellipse2D.Double(rf.getPnt().coord(0)-3,rf.getPnt().coord(1)-3,6,6));
					//}
					//for(Line2 borderLine : ag.getPathArea().getAreaBorderLines()){
					//	g2d.draw(borderLine);
					//}
				}
				/*if(ag.getPath() != null){
					g2d.setColor(Color.red);
					Pnt[] pathPnts = ag.getPath().getPointsOnEdge();
					for(int i = 1; i < pathPnts.length;i++){
						g2d.draw(new Line2(pathPnts[i-1],pathPnts[i]));
					}
				}*/
				g2d.setColor(Color.black);
				/*if(ag.getIntermediateGoal() != null){
					g2d.draw(new Ellipse2D.Double(ag.getIntermediateGoal().coord(0)-3,ag.getIntermediateGoal().coord(1)-3,6,6));
				}*/
				/*if(ag.pathArea.getAreaBorder().size() != 0){
					g2d.setColor(Color.red);
					for(RadialFace rf : ag.pathArea.getAreaBorder()){
						g2d.draw(new Ellipse2D.Double(rf.getPnt().coord(0)-3,rf.getPnt().coord(1)-3,6,6));
						g2d.draw(rf.getBorderLine());
					}
				}*/
				//for(Pnt obsPnt : ag.obs){
					//Line2 line = new Line2(obsPnt,ag.getPosition());
					//g2d.draw(line); //????
				//}
			}
		} catch (ConcurrentModificationException e){
			System.out.println("agent except");
		}
/*		g2d.setColor(Color.red);
		for(int i = 0; i < obs.getObstacleCount(); i++){
			for(int j = 0; j < obs.getInputObstacles().get(i).length;j++){
				int k = j-1;
				if(k < 0){
					k = obs.getInputObstacles().get(i).length - 1;
				}
				Pnt pnt1 = obs.getInputObstacles().get(i)[j];
				Pnt pnt2 = obs.getInputObstacles().get(i)[k];
				g2d.draw(new Line2(pnt1,pnt2));
			}
		}*/
		/*for(Pnt key : coarseGraph.getTheNeighbors().keySet()){
			for(Pnt key2 : coarseGraph.getTheNeighbors().get(key).keySet()){
				Edge edge = coarseGraph.getTheNeighbors().get(key).get(key2);
				Pnt[] pnts = edge.getPointsOnEdge();
				double totalLength = 0.0;
				for(int i = 0; i < pnts.length-1;i++){
					totalLength += pnts[i].distanceTo(pnts[i+1]);
				}
				int x = (int)((edge.getFirstPoint().coord(0)+edge.getLastPoint().coord(0))/2.0);
				int y = (int)((edge.getFirstPoint().coord(1)+edge.getLastPoint().coord(1))/2.0);
				g2d.drawString(((int)totalLength)+"",x,y);
			}
		}	*/
		/*int counter = 0;
		for(Pnt key : fineGraph.getTheNeighbors().keySet()){
			for(Pnt key2 : fineGraph.getTheNeighbors().get(key).keySet()){
				Edge edge = fineGraph.getTheNeighbors().get(key).get(key2);
				int x = (int)((edge.getFirstPoint().coord(0)+edge.getLastPoint().coord(0))/2.0);
				int y = (int)((edge.getFirstPoint().coord(1)+edge.getLastPoint().coord(1))/2.0);
				
				//draw radius circles
				double minDistRadius = edge.getClosestObstacle();
				//g2d.draw(new Ellipse2D.Double(x-minDistRadius,y-minDistRadius,minDistRadius*2,minDistRadius*2));
				//g2d.drawString(((int)edge.getWeight())+" "+(int)minDistRadius,x,y);
				
				//g2d.drawString(((int)edge.getAgentsOnEdge())+"",x,y);
				//for(int o = 0; o < obs.getPolygonCount(); o++){
				//	Pnt middle = edge.getFirstPoint().getMiddle(edge.getLastPoint());
				//	Pnt closest = obs.getClosestPoint(o, middle);
				//	Pnt drawMiddle = middle.getMiddle(closest);
				//	Line2 near = new Line2(middle,closest);
				//	g2d.drawString((int)near.length()+"", (int)drawMiddle.coord(0), (int)drawMiddle.coord(1));
					//Line2 near = obs.getClosestLine(o, new Line2(edge.getFirstPoint(),edge.getLastPoint()));
				//	g2d.drawLine((int)near.x1, (int)near.y1, (int)near.x2, (int)near.y2);
				//}
				//break;
			}
			//break;
		}*/
		//paint path for each agent
		/*for(Agent ag : agents){
			Pnt prevPnt = null;
			if(ag.getPath() != null){
				for(Pnt pathPnt : ag.getPath().getPointsOnEdge()){
					if(prevPnt == null){
						prevPnt = pathPnt;
					} else {
						Line2 line = new Line2(pathPnt,prevPnt);
						g2d.draw(line);
						prevPnt = pathPnt;
					}
				}
			}
		}*/

//		g2d.draw(new Ellipse2D.Double(300.0,200.0,20.0,20.0));
		g2d.setColor(Color.red);
		for(int i = 0; i < scenarios;i++){
			g2d.drawString(fitnesses.get(i)+"  ", 5+i*55 , 18);
		}
		g2d.drawString("Scenario Cycles : "+scenarioCycles, this.getBounds().width - 130 , 8);
		if(running){
			g2d.drawString(OLSet.getCps()+" CPS", 5, 8);
		} else {
			g2d.drawString("Done", 5, 10);
		}
	}

	protected void clear(Graphics g) {
		super.paintComponent(g);
	}
	
	public void updateGraph() {
		graphLines.clear();
		for(Pnt key : sGB.getGraph().getTheNeighbors().keySet()){
			for(Pnt key2 : sGB.getGraph().getTheNeighbors().get(key).keySet()){
				Edge edge = sGB.getGraph().getTheNeighbors().get(key).get(key2);
				Line2 line = new Line2(edge.getFirstPoint().coord(0), edge.getFirstPoint().coord(1), edge.getLastPoint().coord(0), edge.getLastPoint().coord(1));
				graphLines.add(line);
			}
		}		
	}

	public PointGraph getGraph() {
		return sGB.getGraph();
	}

	public synchronized void setAgentScenario(int scenario) {
		currentScenario = scenario;
		agentsSpawned = new ArrayList<ArrayList<Boolean>>();
		for(int i = 0; i < sides; i++){
			ArrayList<Boolean> agentSpawnedSide = new ArrayList<Boolean>();
			for(int j = 0; j < agentScenarios.get(currentScenario).get(i).size();j++){
				agentSpawnedSide.add(false);
			}
			agentsSpawned.add(agentSpawnedSide);
			//System.out.println();
		}
		agents = new ArrayList<Agent>();
	}
	
	private int agentsSpawned(ArrayList<Boolean> agSpawned){
		int i = 0;
		for(Boolean spawned : agSpawned){
			if(spawned){
				i++;
			}
		}
		return i;
	}
	
	private int nextNotSpawned(ArrayList<Boolean> agSpawned, int start){
		int i = start;
		while(i < agSpawned.size()){
			if(!agSpawned.get(i)){
				return i;
			} else {
				i++;
			}
		}
		return -1;
	}
	
	public boolean addAgent(){	
		for(int i = 0; i < agentScenarios.get(currentScenario).size(); i++){
			ArrayList<Agent> side = agentScenarios.get(currentScenario).get(i);
			if(side.size() > agentsSpawned(agentsSpawned.get(i))){
				int nextAg = nextNotSpawned(agentsSpawned.get(i),0);
				boolean clear = false;
				int loopcounter = 0;
				while(nextAg > -1 && !clear){
					Agent ag = side.get(nextAg);
					clear = true;
					ag.setSimpleObs(simpleObs);
					for(Agent otherAg : agents){
						if(ag.overlaps(otherAg)){
							clear = false;
							break;
						}
					}
					if(clear){
						agents.add(new Agent(agents,ag,rand));
						agentsSpawned.get(i).set(nextAg, true);
					} else {
						nextAg = nextNotSpawned(agentsSpawned.get(i),nextAg+1);
					}
					if(loopcounter > side.size()){
						//break;
					}
					loopcounter++;
				}
			}
		}
		return true;
	}

	public synchronized ArrayList<Agent> getAgents() {
		return agents;
	}

	public void readGenes(Gene geneParent, RandomGen rand,String ID,boolean autoChild) {
		this.clear();
		this.ID = ID;
		this.rand = rand;
		//new child + mutate
		if(autoChild){
			this.gene = geneParent.makeChild(rand);
		} else {
			this.gene = geneParent;
		}
		simpleObs = this.gene.getSimpleObstacles();
	}



	public ArrayList<SimpleObstacle> getSimpleObs() {
		return simpleObs;
	}

	public void setSimpleGraphBuilder(SimpleGraphBuilder sGB) {
		this.sGB = sGB;
	}

	public SimpleGraphBuilder getSimpleGraphBuilder(){
		return sGB;
	}
	
	public void incrementCycles(){
		scenarioCycles++;
	}
	
	public Gene getGenes(){
		return gene;
	}



	public void addAgentScenarios(ArrayList<ArrayList<ArrayList<Agent>>> agentScenarios, ArrayList<Pnt> agentScenarioGoals) {
		this.agentScenarios = new ArrayList<ArrayList<ArrayList<Agent>>>();
		this.agentScenarioGoals = new ArrayList<Pnt>();
		for(ArrayList<ArrayList<Agent>> agentScenario : agentScenarios){
			sides = agentScenario.size();
			ArrayList<ArrayList<Agent>> thisAgentScenario = new ArrayList<ArrayList<Agent>>();
			ArrayList<Agent> agentEnvironment = new ArrayList<Agent>();
			for(ArrayList<Agent> side : agentScenario){
				ArrayList<Agent> thisSide = new ArrayList<Agent>();
				thisAgentScenario.add(thisSide);
				for(Agent ag : side){
					thisSide.add(new Agent(agentEnvironment,ag,rand));
					agentEnvironment.add(thisSide.get(thisSide.size()-1));
				}
				this.agentScenarios.add(thisAgentScenario);
			}
		}
		for(Pnt agentScenarioGoal : agentScenarioGoals){
			this.agentScenarioGoals.add(new Pnt(agentScenarioGoal));
		}
		scenarios = agentScenarios.size();
		for(int i = 0; i < scenarios ; i++){
			fitnesses.put(i,0);
		}
	}

	public int getScenarios(){
		return scenarios;
	}

	public int getCurrentScenario(){
		return currentScenario;
	}

	public ArrayList<Pnt> getAgentGoals() {
		return agentScenarioGoals; 
	}
	
	public int getTotalCycles() {
		return totalCycles;
	}

	public int getScenarioCycles() {
		return scenarioCycles;
	}

	public int getTotalAgentCycles() {
		return totalAgentCycles;
	}

	public int getAgentCycles() {
		return agentCycles;
	}

	public void finishCycle() {
		if(currentScenario == scenarios){
			running = false;
		}
		for(Agent ag : agents){
			agentCycles += ag.getCycles();
		}
		fitnesses.put(currentScenario,agentCycles);
		//fitnessLabels.get(currentScenario).setText(agentCycles+"");
		totalAgentCycles += agentCycles;
		agentCycles = 0;
		totalCycles += scenarioCycles;
		scenarioCycles = 0;
	}

	public void addAgentCycles(int cycles) {
		agentCycles += cycles;
		fitnesses.put(currentScenario,agentCycles);
	}



	public String getID() {
		return ID;
	}



	public void readGenesPlayback(Gene gene) {
		this.clear();
		this.gene = gene;
		simpleObs = this.gene.getSimpleObstacles();
	}



	public void setAgents(ArrayList<Agent> agents) {
		this.agents = agents;
		
	}
	
	public int getAgentsPerScenarioCount(){
		return agentScenarios.get(0).size();
	}

	public int getScenarioCount(){
		return agentScenarios.size();
	}



	public ArrayList<ArrayList<ArrayList<Agent>>> getAgentScenarios() {
		// TODO Auto-generated method stub
		return agentScenarios;
	}



	public String getFitnessesString() {
		String fitnessesString = "";
		for(int i = 0; i < fitnesses.size();i++){
			fitnessesString += fitnesses.get(i).toString() + " ";
		}
		return fitnessesString;

	}
}
